import frameImage from '@/assets/mark/normal.png'
import frameImage2 from '@/assets/mark/warn.png'
import Overlay from './Overlay'
export default class GltfModel2 extends Overlay {
  constructor ({
    sandbox, url, enableCache = true,
    itemData = {},
    position = { x: 0, y: 0, z: 0 },
    rotation = { x: 0, y: 0, z: 0 },
    fontOffect = { x: 0, y: 0, z: 0 },
    markOffect = { x: 0, y: 3, z: 0 },
    markType = 1,
    scale = 1,
    isCastShadow,
    isReceiveShadow,
    isChangeMatMetal = false,
    isDoubleSide = false,
    isAddScene = true,
    roughness = 0.3,
    metalness = 0.7,
    label = '',
    isShowLable = false,
    enableClearCoatMat = false
  }) {
    super()
    this.sandbox = sandbox
    this._itemData = itemData
    this.scene = sandbox.scene
    this.camera = sandbox.camera
    this._enableCache = enableCache
    this.url = url
    this.model = null
    this._position = position
    this._rotation = rotation
    this._scale = scale
    this.isCastShadow = isCastShadow
    this.isReceiveShadow = isReceiveShadow
    this.isChangeMatMetal = isChangeMatMetal
    this.enableClearCoatMat = enableClearCoatMat
    this.isDoubleSide = isDoubleSide
    this.isAddScene = isAddScene
    this.roughness = roughness
    this.metalness = metalness
    this.label = label
    this.isShowLable = isShowLable
    this.fontOffect = fontOffect
    this.markOffect = markOffect
    this._markType = markType
    this.actionMap = {
      translate: ['xTranslate', 'yTranslate', 'zTranslate'],
      rotation: ['xRotate', 'yRotate', 'zRotate'],
      scale: ['xScale', 'yScale', 'zScale'],
      uniformScale: ['uniformScale']
    }
    this.loader = window.sandbox.gltfLoader
    this.fontLoader = this.sandbox.fontLoader
    this.activeTweens = new WeakMap()
    this.textMesh = null
    return this.initOverlay()
  }

  async initOverlay () {
    // this.model = await this.loader.loadAsync(this.url)
    this.model = await this.loader.loadResource(this.url, this._enableCache)
    this.overlay = this.model.scene
    this.setModelNodeMap(this.model)
    this.setMarerial()

    // const box = new GC.Box3().setFromObject(this.overlay);
    // const center = new GC.Vector3();
    // box.getCenter(center);
    // const group = new GC.Group();
    // // 调整模型位置
    // this.overlay.position.sub(center);
    // group.add(this.overlay);
    // group.position.copy(center);

    // console.log('this.overlay', this.overlay)
    const { x, y, z } = this._position
    this.overlay.position.set(x, y, z)
    this.overlay.rotation.set(
      GC.MathUtils.degToRad(this._rotation.x), // 将度转换为弧度
      GC.MathUtils.degToRad(this._rotation.y),
      GC.MathUtils.degToRad(this._rotation.z)
    )

    if (this.isAddScene) {
      this.scene.add(this.overlay)
    }
    if (this.isShowLable) {
      this.addText(this.fontPos, this.label)
    }
    this.changeMaterialColor()
    return this
  }

  setModelNodeMap (model) {
    const modelNodeMap = []
    model.scene.traverse(child => {
      if (Object.keys(child.userData).length && child.userData.hasOwnProperty('gltfExtensions')) {
        // 网格节点名称(建模软件中设置的名称或者是对应的零件)
        const modelMeshNodesName = child.userData.name

        // 自定义节点命名(在设置gmdf时设置的名称)
        const articulationName = child.userData.gltfExtensions.AGI_articulations.articulationName

        const index = modelNodeMap.findIndex(item => item?.articulationName === articulationName)
        if (index > -1) {
          modelNodeMap[index].modelMeshNodesName.push(modelMeshNodesName)
        } else {
          modelNodeMap.push({
            modelMeshNodesName: new Array(modelMeshNodesName),
            articulationName
          })
        }
      }
    })

    // 再格外处理添加到自定义关节数据上面
    const articulations = this.model.userData?.gltfExtensions?.AGI_articulations?.articulations || []
    if (modelNodeMap.length && articulations.length) {
      modelNodeMap.forEach(item => {
        const findItem = articulations.find(j => j.name === item.articulationName)
        findItem.modelMeshNodesName = item.modelMeshNodesName
      })
    }
    // console.log('处理后articulations', articulations)
    // console.log('处理后modelNodeMap', modelNodeMap)
    model.modelNodeMap = modelNodeMap
    return modelNodeMap
  }

  setMarerialOpacity (opacityValue = 0.1, color = '#2e547b') {
    const hexColor = new GC.Color(color)
    this.overlay.traverse(child => {
      if (child.isMesh) {
        if (!child.OriMaterial) {
          child.OriMaterial = child.material.clone()
        }
        child.material = new GC.MeshStandardMaterial({
          map: child.material.map,
          transparent: true,
          opacity: opacityValue,
          alphaTest: 0.1,
          color: hexColor
          // color: child.material.color
        })
        // child.material.transparent=true
        // child.material.opacity=0.1
        // child.material.alphaTest=0.1
        child.material.needsUpdate = true
      }
    })
  }

  // 恢复默认材质
  ResetMaterial () {
    this.overlay.traverse(child => {
      if (child.isMesh) {
        if (child.OriMaterial) {
          child.material = child.OriMaterial
          child.material.needsUpdate = true
        }
      }
    })
  }

  setMarerial () {
    // if (this.enableClearCoatMat)
    // {
    //   window.sandbox.logger.log('设置清漆',this.modelName)
    // }
    this.overlay.traverse(child => {
      if (child.isObject3D) {
        child.castShadow = true// this.isCastShadow
        child.receiveShadow = this.isReceiveShadow
      }
      if (child.isMesh) {
        // 将材质的 side 属性设置为 GC.DoubleSide
        if (this.isDoubleSide) {
          child.material.side = GC.DoubleSide
        }
        if (this.isDoubleSide) {
          // 如果材质是多材质数组，也需要遍历设置
          if (Array.isArray(child.material)) {
            child.material.forEach(material => {
              material.side = GC.DoubleSide
            })
          }
        }
        if (this.enableClearCoatMat) {
          // window.sandbox.logger.log('设置清漆')
          if (Array.isArray(child.material)) {
            child.material.forEach(material => {
              material = this.getClearCoatMat(child.material.color)
            })
          } else {
            child.material = this.getClearCoatMat(child.material.color)
          }
        } else if (this.isChangeMatMetal) {
          if (Array.isArray(child.material)) {
            child.material.forEach(material => {
              material.roughness = this.roughness
              material.metalness = this.metalness
            })
          } else {
            child.material.roughness = this.roughness
            child.material.metalness = this.metalness
          }
        }
        child.material.needsUpdate = true
      }
    })
  }

  getClearCoatMat (OriColor, matName = '') {
    const physicalMaterial = new GC.MeshPhysicalMaterial({
      color: OriColor,
      roughness: 0.3,
      metalness: 0.7,
      clearcoat: 0.5, // 清漆效果
      clearcoatRoughness: 0.1,
      ior: 1
    })
    physicalMaterial.name = matName

    // const physicalMaterial = new GC.MeshStandardMaterial({
    //   color: OriColor,
    //   roughness: 0.3,
    //   metalness: 0.7
    // })
    physicalMaterial.name = matName
    return physicalMaterial
  }

  ralToHex (ral) {
    return this.RAL_MAP[ral] ?? '#000000' // 找不到返回黑色
  }

  changeMaterialColor () {
    this.RAL_MAP = {
      'RAL 5015': {
        replaceColor: '#004dfe',
        materialNames: ['#0000ffff', '#0080ffff', '#0099ffff', '#1e2d6eff']
      },
      'RAL 1021': {
        replaceColor: '#e25303',
        materialNames: ['#ff8000ff', '#e77108ff', '#ff7f00ff.002']
      },
      'RAL 1016': {
        replaceColor: '#FFEB00',
        materialNames: ['#ffff00ff']
      },
      'RAL 2002': {
        replaceColor: '#C1121C',
        materialNames: ['#C1121d']
      },
      'RAL 2004': {
        replaceColor: '#E6000A',
        materialNames: ['#E60000']
      }
    }
    this.NAME_TO_RAL = {}
    Object.entries(this.RAL_MAP).forEach(([ral, rule]) => {
      rule.materialNames.forEach(n => this.NAME_TO_RAL[n] = ral)
    })

    // console.log(this.NAME_TO_RAL)
    this.overlay.traverse(child => {
      if (!child.isMesh) return
      // console.log(child.material, this.colorToHex(child.material.color))
      // console.log(child.material.roughness, child.material.metalness, child.material.clearcoat,
      //   child.material.clearcoatRoughness, child.material.ior)
      const mats = Array.isArray(child.material) ? child.material : [child.material]
      mats.forEach(mat => {
        // 逐条规则匹配（支持包含）
        for (const [name, ral] of Object.entries(this.NAME_TO_RAL)) {
          if (mat.name.includes(name)) {
            // const hexColor = new GC.Color(this.RAL_MAP[ral].replaceColor);
            // const hexColor = new GC.Color('#0078B4');//#003074
            const colorRgb = this.hexToRgb01(this.RAL_MAP[ral].replaceColor)
            const hexColor = new GC.Color(colorRgb.r, colorRgb.g, colorRgb.b)// #003074
            // mat.color.copy(hexColor);
            // mat.needsUpdate = true;
            child.material = this.getClearCoatMat(hexColor, 'replace_' + this.RAL_MAP[ral].replaceColor)
            child.material.needsUpdate = true
            break // 命中即停，避免重复赋值
          }
        }
      })
    })
  }

  hexToRgb01 (hex) {
    hex = hex.replace(/^#/, '') // 去掉 #
    if (hex.length !== 6) throw new Error('Invalid HEX')

    const r = parseInt(hex.slice(0, 2), 16) / 255
    const g = parseInt(hex.slice(2, 4), 16) / 255
    const b = parseInt(hex.slice(4, 6), 16) / 255

    return { r, g, b }
  }

  colorToHex (colorObj) {
    // colorObj 可以是 THREE.Color 或 GC.Color
    const r = Math.round(colorObj.r * 255)
      .toString(16)
      .padStart(2, '0')
    const g = Math.round(colorObj.g * 255)
      .toString(16)
      .padStart(2, '0')
    const b = Math.round(colorObj.b * 255)
      .toString(16)
      .padStart(2, '0')
    return `#${r}${g}${b}`.toUpperCase()
  }

  setArticulationStage (articulationStageKey, value, isTween = true) {
    const names = articulationStageKey.split(' ')
    const articulations = this.model.userData?.gltfExtensions?.AGI_articulations?.articulations
    if (names.length !== 2) {
      throw new Error('参数一格式错误')
    } else if (typeof Number(value) !== 'number') {
      throw new Error('第二个参数类型有误')
    } else if (!articulations) {
      throw new Error('该模型没有设置运动关节数据' + this._itemData.dataPath)
    }

    const articulationName = names[0]
    const stageName = names[1]

    // 1.找动作：找到对应的运动关节节点--对应的动作
    let stage = null
    const findItem = articulations.find(item => item.name === articulationName)
    if (findItem) {
      // 在对应的节点下找对应的动作
      stage = findItem.stages.find(item => item.name === stageName)
      if (!stage) {
        throw new Error('"关节动作名称"输入错误')
      }
    } else {
      throw new Error('"关节名称"输入错误')
    }

    // 2.找零部件：根据映射关系匹配输入名称对应的关节零件数组
    const modelNodes =
      this.model.modelNodeMap.find(m => {
        return m.articulationName === articulationName
      })?.modelMeshNodesName || []

    // 超过阈值则不更新
    if (value < stage.minimumValue || value > stage.maximumValue) return

    modelNodes.forEach(n => {
      const node = this.model.scene.getObjectByName(n)
      // console.log('node', node)
      node.originalPosition = node.originalPosition || node.position.clone()
      node.originalRotation = node.originalRotation || node.rotation.clone()
      node.originalScale = node.originalScale || node.scale.clone()

      // todo 自动更新,待修同步cesium的更新机制
      const axis = stage.type.charAt(0) // 获取轴 ('x', 'y', 'z')
      stage.initialValue = value
      // console.log('stage', stage,node)
      if (isTween) {
        this.twinUploadPos(node, axis, stage.type, value)
        // console.log(node.position)
      } else {
        this.uploadPos(node, axis, stage.type, value)
      }
    })
  }

  // 更新骨骼节点位置
  /*
   * @param node  关节节点
   * @param axis 轴线
   * @returns type 关节数据中定义的类型：'xTranslate', 'yTranslate', 'zTranslate'
   * @returns value 运动值
   */
  uploadPos (node, axis, type, value) {
    if (this.actionMap.translate.includes(type)) {
      node.position[axis.toLowerCase()] = value + node.originalPosition[axis.toLowerCase()]
    } else if (this.actionMap.rotation.includes(type)) {
      node.rotation[axis.toLowerCase()] = (value * Math.PI) / 180 + node.originalRotation[axis.toLowerCase()]
    } else if (this.actionMap.scale.includes(type)) {
      node.scale[axis.toLowerCase()] = value + node.originalScale[axis.toLowerCase()]
    } else if (type === 'uniformScale') {
      // 需要加上原始值
      node.scale.set(value + node.originalScale.x, value + node.originalScale.y, value + node.originalScale.z)
    }
  }

  // 在类中定义一个 WeakMap 来存储动画（放在构造函数或类顶部）
  // 格式：{ node: { "position_x": tween, "rotation_y": tween, ... } }
  // 关键点总结
  // WeakMap 初始化：在类构造函数中 this.activeTweens = new WeakMap()。

  // 存储动画：用 WeakMap 按节点存储动画，避免内存泄漏。

  // 停止旧动画：如果同一个属性再次被修改，先停止之前的动画。

  // 启动新动画：用 TWEEN.Tween() 创建平滑过渡动画。

  // 清理动画：onComplete 时从 WeakMap 移除动画引用。
  twinUploadPos (node, axis, type, value) {
    // 初始化当前节点的动画存储
    if (!this.activeTweens.has(node)) {
      this.activeTweens.set(node, new Map())
    }
    const nodeTweens = this.activeTweens.get(node)

    const axisLower = axis.toLowerCase()
    const duration = 1000 // 动画持续时间（毫秒），可以调整

    // 处理位移（position）
    if (this.actionMap.translate.includes(type)) {
      const targetPos = value + node.originalPosition[axisLower]
      const tweenKey = `position_${axisLower}` // 例如 "position_x"

      // 如果已有动画，先停止
      if (nodeTweens.has(tweenKey)) {
        nodeTweens.get(tweenKey).stop()
      }

      // 创建新动画
      const tween = new GC.TWEEN.Tween(node.position)
        .to({ [axisLower]: targetPos }, duration) // 例如 { x: 10 }
        .onComplete(() => nodeTweens.delete(tweenKey)) // 动画完成后清理
        .start()

      nodeTweens.set(tweenKey, tween) // 存储动画
    }

    // 处理旋转（rotation，角度转弧度）
    else if (this.actionMap.rotation.includes(type)) {
      const targetRot = (value * Math.PI) / 180 + node.originalRotation[axisLower]
      const tweenKey = `rotation_${axisLower}`

      if (nodeTweens.has(tweenKey)) {
        nodeTweens.get(tweenKey).stop()
      }

      const tween = new GC.TWEEN.Tween(node.rotation)
        .to({ [axisLower]: targetRot }, duration)
        .onComplete(() => nodeTweens.delete(tweenKey))
        .start()

      nodeTweens.set(tweenKey, tween)
    }

    // 处理缩放（scale）
    else if (this.actionMap.scale.includes(type)) {
      const targetScale = value + node.originalScale[axisLower]
      const tweenKey = `scale_${axisLower}`

      if (nodeTweens.has(tweenKey)) {
        nodeTweens.get(tweenKey).stop()
      }

      const tween = new GC.TWEEN.Tween(node.scale)
        .to({ [axisLower]: targetScale }, duration)
        .onComplete(() => nodeTweens.delete(tweenKey))
        .start()

      nodeTweens.set(tweenKey, tween)
    }

    // 处理 uniformScale（整体缩放）
    else if (type === 'uniformScale') {
      const targetScale = {
        x: value + node.originalScale.x,
        y: value + node.originalScale.y,
        z: value + node.originalScale.z
      }
      const tweenKey = 'scale_uniform'

      if (nodeTweens.has(tweenKey)) {
        nodeTweens.get(tweenKey).stop()
      }

      const tween = new GC.TWEEN.Tween(node.scale)
        .to(targetScale, duration)
        .onComplete(() => nodeTweens.delete(tweenKey))
        .start()

      nodeTweens.set(tweenKey, tween)
    }
  }

  // 通过mesh物体名称获取当前骨骼点的数据
  getArticulationByMeshName (name) {
    // 注意mesh名称不等于关节定义的名称
    // name 为mesh的名称，下面已自定义匹配模型关节名称
    try {
      const result = this.model.modelNodeMap.find(item => item.modelMeshNodesName.includes(name))
      // console.log('xcxcx', result)
      const articulationName = result ? result.articulationName : ''
      const articulations = this.model.userData?.gltfExtensions?.AGI_articulations?.articulations
      if (articulationName && articulations.length) {
        const findItem = articulations.find(item => item.name === articulationName)
        if (findItem) {
          findItem.modelMeshNodesName = result.modelMeshNodesName
        }
        // console.log('findItem', findItem)
        return findItem || []
      } else {
        return []
      }
    } catch (er) {
      return []
    }
  }

  // 修改原始坐标位置
  setArticulationOriginData (articulationStageKey, value) {
    const names = articulationStageKey.split(' ')
    const articulations = this.model.userData?.gltfExtensions?.AGI_articulations?.articulations
    if (names.length !== 2) {
      throw new Error('参数一格式错误')
    } else if (typeof Number(value) !== 'number') {
      throw new Error('第二个参数类型有误')
    } else if (!articulations) {
      throw new Error('该模型没有设置运动关节数据')
    }

    const articulationName = names[0]
    const stageName = names[1]

    // 1.找动作：找到对应的运动关节节点--对应的动作
    let stage = null
    const findItem = articulations.find(item => item.name === articulationName)
    if (findItem) {
      // 在对应的节点下找对应的动作
      stage = findItem.stages.find(item => item.name === stageName)
      if (!stage) {
        throw new Error('"关节动作名称"输入错误')
      }
    } else {
      throw new Error('"关节名称"输入错误')
    }

    // 2.找零部件：根据映射关系匹配输入名称对应的关节零件数组
    const modelNodes =
      this.model.modelNodeMap.find(m => {
        return m.articulationName === articulationName
      })?.modelMeshNodesName || []

    modelNodes.forEach(n => {
      const node = this.model.scene.getObjectByName(n)
      const axis = stage.type.charAt(0) // 获取轴 ('x', 'y', 'z')
      stage.initialValue = 0
      this.setOriginaData(node, axis, stage.type, value)
    })
  }

  setOriginaData (node, axis, type, value) {
    if (this.actionMap.translate.includes(type)) {
      // window.sandbox.logger.log('原始', node.name, node.position[axis.toLowerCase()])
      node.position[axis.toLowerCase()] = value
      // node.position[axis.toLowerCase()] = value + node.position[axis.toLowerCase()]
      node.originalPosition = node.originalPosition || node.position.clone()
      //  window.sandbox.logger.log('修改原始', node.name, node.originalPosition[axis.toLowerCase()])
    } else if (this.actionMap.rotation.includes(type)) {
      // window.sandbox.logger.log('原始rotation', node.name, this.radiansToDegrees(node.rotation.x))
      node.rotation[axis.toLowerCase()] = (value * Math.PI) / 180
      node.originalRotation = node.originalRotation || node.rotation.clone()
      // window.sandbox.logger.log('修改原始rotation', node.name, this.radiansToDegrees(node.originalRotation[axis.toLowerCase()]))
    } else if (this.actionMap.scale.includes(type)) {
      window.sandbox.logger.log('原始rotation', node.name, node.scale)
    } else if (type === 'uniformScale') {
      // 需要加上原始值
      // node.scale.set(value + node.originalScale.x, value + node.originalScale.y, value + node.originalScale.z)
    }
  }

  radiansToDegrees (radians) {
    return radians * (180 / Math.PI)
  }

  zoomTo () {
    const box = new GC.Box3().setFromObject(this.overlay)
    const size = box.getSize(new GC.Vector3())
    const center = box.getCenter(new GC.Vector3())

    // 设置相机位置和朝向
    const maxSize = Math.max(size.x, size.y, size.z)
    const cameraDistance = maxSize * 3.0
    this.sandbox.camera.position.set(center.x + cameraDistance, center.y + cameraDistance / 2, center.z + cameraDistance / 2)
    this.sandbox.camera.lookAt(center.x, center.y, center.z)
  }

  get modelName () {
    const filePath = this.url
    const lastSlashIndex = filePath.lastIndexOf('/')
    const fileNameWithExt = filePath.substring(lastSlashIndex + 1)

    const lastDotIndex = fileNameWithExt.lastIndexOf('.')
    return fileNameWithExt.substring(0, lastDotIndex)
  }

  addText (fontPos, label) {
    return new Promise((resolve, reject) => {
      // Handle potential errors in font loading
      this.fontLoader.load(
        window.modelUrl + '/fonts/HONOR_Sans_CN_Regular.json',
        (font) => {
          try {
            const geometry = new GC.TextGeometry(label, {
              font,
              size: 0.5,
              depth: 0.1
            })

            geometry.center()

            const material = new GC.MeshBasicMaterial({
              color: '#FF0000',
              transparent: true,
              opacity: 1
            })

            this.textMesh = new GC.Mesh(geometry, material)
            this.textMesh.position.copy(fontPos)
            this.textMesh.rotation.set(Math.PI, Math.PI, Math.PI)

            this.scene.add(this.textMesh)

            // Store the animation function reference for later cleanup
            this.animateTextMesh = () => {
              if (this.textMesh) {
                this.textMesh.lookAt(this.camera.position)
                this.animationId = requestAnimationFrame(this.animateTextMesh)
              }
            }

            this.animateTextMesh()
            resolve(this.textMesh)
          } catch (error) {
            reject(error)
          }
        },
        undefined, // Progress callback (optional)
        (error) => reject(error) // Error callback
      )
    })
  }

  stopAnimation () {
    if (this.animationId) {
      cancelAnimationFrame(this.animationId)
      this.animationId = null
    }
    // Clean up the animation function reference
    if (this.textMesh) {
      this.scene.remove(this.textMesh)
      this.textMesh = null
    }
  }

  get fontPos () {
    const boundingBox = new GC.Box3().setFromObject(this.overlay)
    // const size = boundingBox.getSize(new GC.Vector3())
    const center = boundingBox.getCenter(new GC.Vector3())
    const resPos = new GC.Vector3(center.x + this.fontOffect.x, center.y + this.fontOffect.y, center.z + this.fontOffect.z)
    return resPos
  }

  get markPos () {
    const boundingBox = new GC.Box3().setFromObject(this.overlay)
    // const size = boundingBox.getSize(new GC.Vector3())
    const center = boundingBox.getCenter(new GC.Vector3())
    const resPos = new GC.Vector3(center.x + this.markOffect.x, center.y + this.markOffect.y, center.z + this.markOffect.z)
    return resPos
  }

  // #region 创建设备标签
  async createMark () {
    if (!this._itemData.idDevice) return
    if (this.mark) return
    const item = {
      position: this.markPos,
      rotation: { x: 0, y: 180, z: 0 },
      scale: 0.01,
      lable: '正在运行',
      image: frameImage,
      imageWidth: '50px',
      imageHigh: '87px'
    }
    this.mark = await new GCMap.Css3DLable({
      sandbox: window.sandbox,
      position: item.position,
      rotation: item.rotation,
      scale: item.scale,
      text: item.lable,
      image: item.image,
      imageWidth: item.imageWidth,
      imageHigh: item.imageHigh,
      EnableLookAtCamera: true,
      fountOffect: { x: 0, y: -150, z: 0 },
      fontSize: '0px',
      ClickcallBack: this.Click3DLable
    })
    this.updateMarkImage()
    this._updateMark = this.updateLabel.bind(this)
    window.sandbox.renderCalls.push(this._updateMark)
  }

  updateLabel () {
    if (this.mark) {
      this.mark.position = this.markPos
    }
  }

  destroyMark () {
    if (!this.mark) return
    const index = this.sandbox.renderCalls.indexOf(this._updateMark)
    if (index > -1) {
      this.sandbox.renderCalls.splice(index, 1) // 从找到的索引位置移除1个元素
    }
    this.mark.destroy()
    this.mark = null
  }

  updateMarkText (label) {
    this.mark.updateText(label)
  }

  updateMarkImage () {
    if (!this.mark) return
    this.image = frameImage
    switch (this._markType) {
      case 1:
        this.image = frameImage
        break
      case 2:
        this.image = frameImage2
        break
    }
    this.mark.updateImage(this.image)
  }

  get markType () {
    return this._markType
  }

  set markType (value) {
    this._markType = value
    this.updateMarkImage()
  }

  Click3DLable (target, id) {
    console.log('点击', id)
  }
  // #endregion
}
